﻿using Clancy.Data;
using Clancy.Interfaces;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Clancy.Bl
{
    public class SurveilanceLogic : ISurveilanceLogic
    {
        private readonly IRecordingManager m_RecordingManager;

        private readonly Dictionary<ISurveilanceObject, Surveilance.Surveilance> m_Surveilances =
            new Dictionary<ISurveilanceObject, Surveilance.Surveilance>();

        private readonly ISurveilanceStorage m_SurveilanceStorage;
        private readonly IVideoSourceFactory m_VideoSourceFactory;

        private readonly Dictionary<ISurveilanceObject, IVideoSourceHandler> m_VideoSources =
            new Dictionary<ISurveilanceObject, IVideoSourceHandler>();

        private bool m_Disposed;
        private bool m_Disposing;

        public SurveilanceLogic(ISurveilanceStorage surveilanceStorage, IVideoSourceFactory videoSourceFactory, IRecordingManager recordingManager)
        {
            m_SurveilanceStorage = surveilanceStorage;
            m_VideoSourceFactory = videoSourceFactory;
            m_RecordingManager = recordingManager;
        }

        public ISurveilanceObject AddNewSource(ISurveilanceObject surveilanceObject)
        {
            if (surveilanceObject == null)
                return null;

            Surveilance.Surveilance newSurveilance = new Surveilance.Surveilance(surveilanceObject, (survObj, filepath) =>
                m_RecordingManager.InsertNewRecord(survObj, filepath));

            IVideoSourceHandler videoSourceHandler = m_VideoSourceFactory.GetVideoSourceHandler(surveilanceObject);
            videoSourceHandler.IncomingBitmapFrame.Subscribe(newSurveilance);
            videoSourceHandler.Open();

            m_Surveilances[surveilanceObject] = newSurveilance;
            m_VideoSources[surveilanceObject] = videoSourceHandler;
            m_SurveilanceStorage.SurveilanceObjects.Add(surveilanceObject);

            return surveilanceObject;
        }

        public ISurveilanceObject AddNewSource(IVideoSourceFilterInfo videoSourceFilterInfo)
        {
            ISurveilanceObject surveilanceObject = new SurveilanceObject();
            surveilanceObject.MonikerString = videoSourceFilterInfo.MonikerString;
            surveilanceObject.Name = videoSourceFilterInfo.Name;
            surveilanceObject.VideoSourceName = videoSourceFilterInfo.Name;
            surveilanceObject.Framerate = videoSourceFilterInfo.Framerate;

            AddNewSource(surveilanceObject);

            return surveilanceObject;
        }

        public Task DeleteSurveilance(ISurveilanceObject surveilanceObject)
        {
            if (surveilanceObject == null)
                return Task.FromResult(false);

            ISurveilanceObject foundSurveilance = m_Surveilances.Keys.FirstOrDefault(surv => surv.Id == surveilanceObject.Id);
            if (foundSurveilance == null)
                return Task.FromResult(false);

            m_VideoSourceFactory.ReleaseVideoSource(foundSurveilance.MonikerString);

            m_Surveilances[foundSurveilance].Dispose();
            m_Surveilances.Remove(foundSurveilance);

            m_SurveilanceStorage.SurveilanceObjects.Remove(foundSurveilance);

            return Task.FromResult(true);
        }

        public void Dispose()
        {
            if (m_Disposed || m_Disposing)
                return;

            m_Disposing = true;

            CloseAllVideoSources();

            foreach (Surveilance.Surveilance surveilance in m_Surveilances.Values)
            {
                surveilance.Dispose();
            }
            m_Surveilances.Clear();

            m_Disposed = true;
        }

        public ISurveilanceStatus GetSurveilance(ISurveilanceObject surveilanceObject)
        {
            return !m_Surveilances.ContainsKey(surveilanceObject) ? null : m_Surveilances[surveilanceObject];
        }

        public void LoadConfiguration(string filename)
        {
            if (!File.Exists(filename))
                return;

            m_Surveilances.Clear();

            SurveilanceSetup setup = new SurveilanceSetup();
            setup.Load(filename);

            foreach (SurveilanceObject surveilance in setup.SurveilanceObjects)
            {
                AddNewSource(surveilance);
            }
        }

        public void SaveConfiguration(string filename)
        {
            SurveilanceSetup setup = new SurveilanceSetup { SurveilanceObjects = m_SurveilanceStorage.SurveilanceObjects.Cast<SurveilanceObject>().ToArray() };
            setup.Save(filename);
        }

        public void Start()
        {
        }

        public void Stop()
        {
        }

        private void CloseAllVideoSources()
        {
            foreach (ISurveilanceObject surveilanceConfig in m_VideoSources.Keys)
            {
                m_VideoSources[surveilanceConfig].Close();
            }
        }
    }
}